unit Checkers.Field;

interface

uses
  System.Types,
  SysUtils,
  Classes,
//GLScene
  VectorGeometry,
  GLScene,
  GLFilePNG,
  GLMaterial,
  GLObjects,
  GLWindowsFont,
  GLSGenerics,
  GLRenderContextInfo,
//Project
  Checkers.Cell,
  Checkers.Checker,
  Checkers.Borders;

type
  TCellsList = GList<TCell>;
  TField = class(TGLBaseSceneObject)
  private
    FBorder: TCheckersBorder;
    FCellSize: Single;
    FSize: TPoint;
    FInteractiveCells: TCellsList;
    FActiveCells: TCellsList;
    FCellsBuffer: TCellsList;
    FBlackCells: TCellsList;
    FWhiteCells: TCellsList;
    FMaterials: TGLMaterialLibrary;
    FFont: TGLWindowsBitmapFont;
    FCheckersModels: TGLImmaterialSceneObject;
    FIsFlipped: Boolean;
    FShowText: Boolean;
    procedure FillBuffer;
    procedure SetSize(const ASize: TPoint);
    function GetInteractiveCells: TCellsList;
    function GetCells: TCellsList;
    procedure SetMaterials(const Value: TGLMaterialLibrary);
    function GetWidth: Single;
    function GetHeight: Single;
    function GetIsFlipped: Boolean;
    procedure SetIsFlipped(const Value: Boolean);
    function GetShowText: Boolean;
    procedure SetShowText(const Value: Boolean);
    procedure SetIsReversed(const Value: Boolean);
    function GetBorderPlanes: TPlanesList;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DoRender(var ARci: TRenderContextInfo; ARenderSelf: Boolean;
      ARenderChildren: Boolean); override;
    property Size: TPoint write SetSize;
    property InteractiveCells: TCellsList read GetInteractiveCells;
    property Cells: TCellsList read GetCells;
    property WhiteCells: TCellsList read FWhiteCells;
    property BlackCells: TCellsList read FBlackCells;
    property Materials: TGLMaterialLibrary write SetMaterials;
    property FullWidth: Single read GetWidth;
    property FullHeight: Single read GetHeight;
    property IsFlipped: Boolean read GetIsFlipped write SetIsFlipped;
    property IsReversed: Boolean write SetIsReversed;
    property ShowText: Boolean read GetShowText write SetShowText;
    property BorderPlanes: TPlanesList read GetBorderPlanes;
  end;

implementation

uses
  Checkers.Visualizer;

{ TField }

constructor TField.Create(AOwner: TComponent);
begin
  inherited;
  FIsFlipped := False;
  FShowText := False;
  FCellSize := 0.5;
  FBorder := TCheckersBorder.CreateAsChild(Self);
  FActiveCells := TCellsList.Create;
  FCellsBuffer := TCellsList.Create;
  FInteractiveCells := TCellsList.Create;
  FBlackCells := TCellsList.Create;
  FWhiteCells := TCellsList.Create;
  FFont := TGLWindowsBitmapFont.Create(Self);
  FillBuffer;
end;

procedure TField.FillBuffer;
var
  i: Integer;
  vCell: TCell;
begin
  for i := 0 to FIELD_SIZE * 2 do
  begin
    vCell := TCell.CreateAsChild(Self);
    vCell.Visible := False;
    vCell.IsOnBoard := False;
    vCell.CubeWidth := FCellSize;
    vCell.CubeDepth := FCellSize;
    vCell.CubeHeight := 0.01;
    vCell.Font := FFont;
    FCellsBuffer.Add(vCell);
  end;
end;

function TField.GetInteractiveCells: TCellsList;
begin
  Result := FInteractiveCells;
end;

function TField.GetBorderPlanes: TPlanesList;
begin
  Result := FBorder.Planes;
end;

function TField.GetCells: TCellsList;
begin
  Result := FCellsBuffer;
end;

function TField.GetHeight: Single;
begin
  Result := FBorder.FullHeight;
end;

function TField.GetWidth: Single;
begin
  Result := FBorder.FullWidth;
end;

procedure TField.SetSize(const ASize: TPoint);
var
  vX, vY, i, j, vCellID, vBufferID: Integer;
  vCell: TCell;
  vColor: Single;

procedure MakeColor(const ACell: TCell; const AWhite: Boolean);
begin
  with FMaterials.Materials do
  begin
    if AWhite then
    begin
      ACell.Material := GetLibMaterialByName('WhiteCell').Material;
      FWhiteCells.Add(ACell);
    end
    else
    begin
      ACell.Material := GetLibMaterialByName('BlackCell').Material;
      FBlackCells.Add(ACell);
    end;
  end;
end;

begin
  vX := ASize.Y - 1;
  vY := ASize.X - 1;
  FSize := ASize;
  FBorder.SetBoardSize(FCellSize * ASize.Y, FCellSize * ASize.X);
  vCellID := 0;
  vBufferID := 0;
  for i := 0 to FCellsBuffer.Count - 1 do
  begin
    FCellsBuffer[i].IsOnBoard := False;
    FCellsBuffer[i].IsSelectebale := False;
    FCellsBuffer[i].ShowText := False;
  end;
  FActiveCells.Clear;
  FInteractiveCells.Clear;
  FWhiteCells.Clear;
  FBlackCells.Clear;
  for i := 0 to vX do
  begin
    for j := 0 to vY do
    begin
      vCell := FCellsBuffer[vBufferID];
      vCell.IsOnBoard := True;
      if FIsFlipped then
      begin
        vCell.Position.X := FCellSize * vX / 2 - i * FCellSize;
        vCell.Position.Z := j * FCellSize - FCellSize * vY / 2;
      end
      else
      begin
        vCell.Position.X := FCellSize * vX / 2 - i * FCellSize;
        vCell.Position.Z := FCellSize * vY / 2 - j * FCellSize;
      end;
      vCell.Position.Y := -0.4;
      vColor := 1 - (i mod 2 * j mod 2 + Integer(i mod 2 = j mod 2));
      vColor := ClampValue(vColor, 0, 1);
      if (vColor <= 0) then
      begin
        vCell.IsSelectebale := True;
        vCell.ID := vCellID;
        FInteractiveCells.Add(vCell);
        MakeColor(vCell, FIsFlipped);
        vCell.Text := IntToStr(vCellID);
        vCell.ShowText := FShowText;
        vCellID := vCellID + 1;
      end
      else
        MakeColor(vCell, not FIsFlipped);
      FActiveCells.Add(vCell);
      vBufferID := vBufferID + 1;
    end;
  end;
end;

procedure TField.SetMaterials(const Value: TGLMaterialLibrary);
var
  i: Integer;
  vMaterial: TGLLibMaterial;
begin
  FMaterials := Value;
  for i := 0 to FCellsBuffer.Count - 1 do
  begin
    FCellsBuffer[i].Material.MaterialLibrary := FMaterials;
    with FMaterials.Materials do
      vMaterial := GetLibMaterialByName('Highlite');
      if Assigned(vMaterial) then
        FCellsBuffer[i].HighlitePlane.Material := vMaterial.Material
      else
        raise Exception.Create('Can not found cell highlite material');
  end;
end;

function TField.GetIsFlipped: Boolean;
begin
  Result := FIsFlipped;
end;

procedure TField.SetIsFlipped(const Value: Boolean);
begin
  FIsFlipped := Value;
end;

procedure TField.SetShowText(const Value: Boolean);
var
  i: Integer;
begin
  FShowText := Value;
  for i := 0 to FInteractiveCells.Count - 1 do
    FInteractiveCells[i].ShowText := FShowText;
end;

function TField.GetShowText: Boolean;
begin
  Result := FShowText;
end;

procedure TField.SetIsReversed(const Value: Boolean);
var
  i: Integer;
begin
  for i := 0 to FCellsBuffer.Count - 1 do
    FCellsBuffer[i].IsReversed := Value;
end;

procedure TField.DoRender(var ARci: TRenderContextInfo; ARenderSelf,
  ARenderChildren: Boolean);
begin
  RenderChildren(0, Count - 1, ARci);
end;

destructor TField.Destroy;
begin
  FCellsBuffer.Free;
  FActiveCells.Free;
  FInteractiveCells.Free;
  FFont.Free;
  FWhiteCells.Free;
  FBlackCells.Free;
  inherited;
end;

end.
